﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;


namespace DARE
{
    [AttrTypeDependentSerializable(typeof(CListBoxData))]
    public class CListBox : CWidget
    {
        #region fields

        private CVerticalScrollBar m_vScrollBar;
        private List<CWidget> m_list;
        private List<CWidget> m_drawList;
        private float m_progress;
        private int m_selectedElement;
        private float WidgetsHeight;
        private bool m_scrollEnabled;
        private bool m_scrollVisible;

        private CWidget m_selectedListCWidget;

        #endregion

        #region properties

        /// <summary>
        /// Gets the list of widgets contained in this CListBox
        /// </summary>
        public List<CWidget> CWidgetList { get { return m_list; } }

        /// <summary>
        /// Gets/Sets the height of each widget displayed.
        /// </summary>
        public float CWidgetHeight { get { return WidgetsHeight; } set { WidgetsHeight = value; Actualize(); } }

        /// <summary>
        /// Gets the number of items contained
        /// </summary>
        public int Count { get { return m_list.Count; } }

        /// <summary>
        /// Gets the number of items displayed
        /// </summary>
        public int DisplayCount { get { return m_drawList.Count; } }

        /// <summary>
        /// Gets the currently selected widget
        /// </summary>
        public CWidget SelectedInList
        {
            get
            {
                if (m_selectedElement >= 0 && m_selectedElement < m_list.Count)
                    return m_list[m_selectedElement];
                return null;
            }
        }

        #endregion

        #region actualize

        /// <summary>
        /// Gets the index of the first displayed item
        /// </summary>
        /// <returns></returns>
        public int getStartIndex()
        {
            if (m_scrollEnabled && m_list.Count * WidgetsHeight >= m_rect.Height)
            {
                m_scrollVisible = true;
                return (int)((m_vScrollBar.Value * (m_list.Count - Math.Floor(m_rect.Height / WidgetsHeight)) / 100));
            }
            return (0);
        }

        /// <summary>
        /// Gets the max number of displayed items
        /// </summary>
        /// <returns></returns>
        public int getMaxDisplayed()
        {
            return (int)(m_rect.Height / WidgetsHeight);
        }

        /// <summary>
        /// Actualizes the widget.
        /// If anything has changed, it will allow this widget to get back its parameters.
        /// For example, if the size of the widget is a percentage of the size of its parent,
        /// if the parent's size changes, it will recalculate the good size of this widget.
        /// (This method is called automatically but you can force a new actualization).
        /// </summary>
        public override void Actualize()
        {
            base.Actualize();
           
            if (m_list == null)
                return;
            int start = getStartIndex();
            int end = getMaxDisplayed() + start;

            int max = getMaxWidthOfDisplayedList();
            if (m_rect.Width < max)
                m_rect.Width = max;
            if (m_parent != null && m_parent.Rect.Width - m_margins.Left - m_margins.Right > m_rect.Width)
                m_rect.Width = (int)(m_parent.Rect.Width - m_margins.Left - m_margins.Right);
            Rectangle vsr = m_vScrollBar.Rect;
            if ((end - start) < m_list.Count)
            {
                vsr.Width = 15;
                vsr.X = m_rect.X + m_rect.Width - vsr.Width;
                vsr.Y = m_rect.Y;
                vsr.Height = m_rect.Height;
            }
            else
            {
                vsr.Width = 0;
                vsr.X = m_rect.X + m_rect.Width - vsr.Width;
                vsr.Y = m_rect.Y;
                vsr.Height = m_rect.Height;
                m_scrollVisible = false;
            }
            m_vScrollBar.Rect = vsr;
            
            
            m_drawList.Clear();
            foreach (CWidget wid in m_list)
                wid.Hide();
            for (int i = start; i < end && i < m_list.Count; ++i)
            {
                Rectangle r = m_list.ElementAt(i).Rect;
                r.X = (int)(m_rect.X + m_margins.Left);
                if (m_scrollVisible)
                    r.Width = (int)(m_rect.Width - (m_vScrollBar.Rect.Width - (m_margins.Left + m_margins.Right)));
                else
                    r.Width = (int)(m_rect.Width - (m_margins.Left + m_margins.Right));
                r.Height = (int)(WidgetsHeight - (m_margins.Top + m_margins.Bottom));
                r.Y = (int)(((i - start) * WidgetsHeight) + m_rect.Y + m_margins.Top);
                m_list.ElementAt(i).Rect = r;
                m_list.ElementAt(i).Actualize();
                m_list.ElementAt(i).Show();
                m_drawList.Add(m_list.ElementAt(i));
            }
        }

        /// <summary>
        /// Gets the max width of the displayed list (represents the largest widget in the list)
        /// </summary>
        public int getMaxWidthOfDisplayedList()
        {
            int ret = 0;
            foreach (CWidget w in m_drawList)
            {
                float tmp = m_list[m_list.IndexOf(w)].getMaxWidth();
                if (tmp + m_vScrollBar.Rect.Width > ret)
                    ret = (int)tmp + m_vScrollBar.Rect.Width;
            }
            return ret;
        }

        #endregion

        #region actionsOnm_list

        /// <summary>
        /// Clear the widget by removing all its children from the HUDSystem and by removing them.
        /// </summary>
        public override void Clear(bool recursively)
        {
            foreach (CWidget w in m_list)
                w.Clear(recursively);
            m_list.Clear();
            m_drawList.Clear();
            Actualize();
            base.Clear(recursively);
        }

        public override void Clear()
        {
            Clear(true);
        }

        /// <summary>
        /// if the index isn't out of range, this will select the widget at this index
        /// </summary>
        public void SelectWidget(int index)
        {
            if (index >= 0 && index < m_list.Count)
                m_selectedElement = index;
            if (m_selectedElement != -1)
            {
                m_selectedListCWidget = m_list.ElementAt(m_selectedElement);
            }

        }

        /// <summary>
        /// if the widget does exist in this CListBox, this will select it.
        /// else, if the add value is set to true, it will add the widget to the list.
        /// </summary>
        public void SelectWidget(CWidget wid, bool addIfDoesntExist)
        {
            if (addIfDoesntExist && !m_list.Contains(wid))
                Add(wid);
            m_selectedElement = m_list.IndexOf(wid);
            if (m_selectedElement != null)
            {
                m_selectedListCWidget = m_list.ElementAt(m_selectedElement);
            }
        }

        public void SelectWidget(CWidget wid)
        {
            SelectWidget(wid, false);
        }

        /// <summary>
        /// Shows this widget and all its children
        /// </summary>
        public override void Show()
        {
            base.Show();
            Actualize();
        }

        /// <summary>
        /// Hides this widget and all its children
        /// </summary>
        public override void Hide()
        {
            base.Hide();
            foreach (CWidget wid in m_list)
                wid.Hide();
        }

        /// <summary>
        ///  Adds a widget in the list
        /// </summary>
        public CListBox Add(CWidget line)
        {
            m_list.Add(line);
            m_drawList.Add(line);
          //  line.Parent = this;
            line.Hide();
            if (m_scrollEnabled && m_list.Count * WidgetsHeight >= m_rect.Height)
                m_scrollVisible = true;
            Actualize();
            if (m_isVisible)
                Show();
            else
                Hide();
            return this;
        }

        #endregion

        #region ctor

        public CListBox(CHUDSystem hud, CWidget parent, string name)
            : base(hud, parent, name)
        {
            IsInputConsumer = false;
            m_list = new List<CWidget>();
            m_drawList = new List<CWidget>();
            m_texture = HUD.Skinner.GetTexture("defaultwidget");
            m_selectedElement = -1;
            m_vScrollBar = hud.GetElement(name + "_vscrollbar") as CVerticalScrollBar;
            if (m_vScrollBar == null)
                m_vScrollBar = new CVerticalScrollBar(hud, this, name + "_vscrollbar");
            m_vScrollBar.Size = new Vector2(15, m_rect.Height);
            m_vScrollBar.Position = new Vector2(m_rect.Width - 15, 0);
            CWidgetHeight = 30;
            m_scrollEnabled = true;
            m_scrollVisible = false;
            m_progress = m_vScrollBar.Value;
            m_selectedListCWidget = null;
            m_margins.Left = 5;
            HLColor = new Color(50, 124, 165, 255);
            Size = new Vector2(300, 400);
        }

        #endregion

        #region event

        /// <summary>
        /// Gets a widget by its name
        /// </summary>
        public override CWidget GetElement(string name)
        {
            CWidget w = base.GetElement(name);
            if (w == null)
                foreach (CWidget wid in m_list)
                    if (wid.Name == name)
                        return wid;
            return w;
        }

        /// <summary>
        /// Method called whenever the mouse is pressed and is over this widget
        /// </summary>
        public override bool onClick(CMouse mouse)
        {
            base.onClick(mouse);
            foreach (CWidget wid in m_list)
                if (wid.IsVisible && wid.isMouseOver(mouse) && (!m_scrollVisible || !m_vScrollBar.isMouseOver(mouse)))
                {
                    if (m_selectedListCWidget != null)
                        m_selectedListCWidget.IsHighLighted = false;
                    m_selectedListCWidget = wid;
                    InvokeClick(this);
                    m_selectedElement = m_list.IndexOf(m_selectedListCWidget);
                    wid.onClick(mouse);
                    break;
                }
            return true;
        }

        /// <summary>
        /// Method called whenever the mouse is released and is over this widget and was pressed over this widget
        /// </summary>
        public override bool onRelease(CMouse mouse)
        {
            if (m_selectedListCWidget != null)
            {
                if (m_selectedListCWidget.isMouseOver(mouse))
                {
                    int old = m_selectedElement;
                    m_selectedElement = m_list.IndexOf(m_selectedListCWidget);
                    m_selectedListCWidget.onRelease(mouse);
                    InvokeRelease(this);
                    Actualize();
                }
                m_selectedListCWidget = null;
            }
            else
                m_selectedElement = -1;
            return true;
        }

        #endregion

        #region update/draw

        public override bool Update(GameTime gameTime)
        {
            if (!m_isVisible)
                return false;
            if (m_vScrollBar.Position != new Vector2(m_rect.Width - 15, 0))
            {
                m_vScrollBar.Position = new Vector2(m_rect.Width - 15 - m_margins.Left - m_margins.Right, 0);
                m_vScrollBar.Size = new Vector2(15, m_rect.Height);
            }
            if (m_progress != m_vScrollBar.Value)
            {
                m_progress = m_vScrollBar.Value;
                Actualize();
            }
            if (m_selectedListCWidget != null)
                m_selectedListCWidget.HLColor = HLColor;
            return base.Update(gameTime);
        }

        public override bool Draw(GameTime gameTime)
        {
            if (!m_isVisible)
                return false;
            if (m_texture != null)
            {
                m_texture.Rectangle = new Rectangle(m_rect.X, m_rect.Y, m_rect.Width - (m_scrollVisible?m_vScrollBar.Rect.Width:0), m_rect.Height);
                m_texture.Draw(gameTime);
            }
            int i = 0;
            Color c = HUD.Shader.HighLightColor;
            bool isHighLighted = HUD.Shader.IsHighLighted;
            foreach (CWidget elem in m_drawList)
            {
                if (i == m_selectedElement)
                {
                    HUD.Shader.HighLightColor = HLColor;
                    HUD.Shader.IsHighLighted = true;
                }
                elem.Draw(gameTime);
                if (i == m_selectedElement)
                {
                    HUD.Shader.HighLightColor = c;
                    HUD.Shader.IsHighLighted = isHighLighted;
                }
                ++i;
            }
            if (m_scrollVisible)
                m_vScrollBar.Draw(gameTime);
            return true;
        }

        #endregion

    }
}
